package mongodb;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import com.mongodb.*;
import org.bson.Document;

import com.mongodb.MongoClientOptions.Builder;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import com.mongodb.operation.OrderBy;

public class MongoHelper {

	public static MongoHelper INSTANCE;

	private static MongoClient mongoClient;
	private static String server = "localhost";
	private static int port = 27017;

	private MongoHelper() {
		// read properties
		try {
			ResourceBundle resource = ResourceBundle.getBundle("mongodb");
			server = resource.getString("server");
			port = Integer.parseInt(resource.getString("port"));
		} catch (Exception e) {
			e.printStackTrace();
		}

		// boolean auth = mongoClient.authenticate(myUserName, myPassword);
		Builder builder = new MongoClientOptions.Builder();
		// options.autoConnectRetry(true);// 自动重连true
		// options.maxAutoConnectRetryTime(10); // the maximum auto connect
		// retry time
		builder.connectionsPerHost(300);// 连接池设置为300个连接,默认为100
		builder.connectTimeout(15000);// 连接超时，推荐>3000毫秒
		builder.maxWaitTime(5000); //
		builder.socketTimeout(0);// 套接字超时时间，0无限制
		builder.threadsAllowedToBlockForConnectionMultiplier(5000);// 线程队列数
		builder.writeConcern(WriteConcern.ACKNOWLEDGED);// 开启安全模式，如果写失败会获取到异常
		MongoClientOptions options = builder.build();

		mongoClient = new MongoClient(server + ":" + port, options);
	}

	public MongoDatabase getDatabase(String dbName) {
		if (dbName == null || dbName.trim().length() == 0) {
			return null;
		}
		return mongoClient.getDatabase(dbName);
	}

	public List<String> getAllDatabaseNames() {
		return convert(mongoClient.listDatabaseNames());
	}

	public MongoCollection<Document> getCollection(String databaseName, String collectionName) {
		MongoDatabase database = getDatabase(databaseName);
		if (database == null) {
			return null;
		}
		MongoCollection<Document> collection;
		collection = database.getCollection(collectionName);
		// 如果名称为collectionName的MongoCollection不存在，则新建
		if (collection == null) {
			database.createCollection(collectionName);
			collection = database.getCollection(collectionName);
		}
		return collection;
	}

	public List<String> getAllCollectionNames(String dbName) {
		MongoDatabase database = getDatabase(dbName);
		if (database == null) {
			return null;
		}

		return convert(database.listCollectionNames());
	}

	public BasicDBObject condition(MongoOperationEnum con, String filedName, Object fieldValue) {
		if (con == MongoOperationEnum.AND || con == MongoOperationEnum.OR) {
			throw new IllegalArgumentException();
		}
		return new BasicDBObject(filedName, new BasicDBObject(con.getValue(), fieldValue));
	}

	public BasicDBObject condition(MongoOperationEnum con, BasicDBObject... objs) {
		if (con != MongoOperationEnum.AND && con != MongoOperationEnum.OR) {
			throw new IllegalArgumentException();
		}
		return new BasicDBObject(con.getValue(), Arrays.asList(objs));
	}

	public List<Document> find(String databaseName, String collectionName, BasicDBObject condition) {
		return find(databaseName, collectionName, condition, null, null, null, null);
	}

	public List<Document> find(String databaseName, String collectionName, BasicDBObject condition,
			String sortFieldName, OrderBy order, Integer pageNo, Integer pageSize) {
		int orderInt = order == null ? -1 : order.getIntRepresentation();
		FindIterable<Document> result = condition == null ? getCollection(databaseName, collectionName).find()
				: getCollection(databaseName, collectionName).find(condition);
		if (sortFieldName != null) {
			result.sort(new BasicDBObject(sortFieldName, orderInt));
		}
		if (pageNo != null && pageSize != null) {
			result.skip((pageNo.intValue() - 1) * pageSize.intValue()).limit(pageSize.intValue());
		}
		return convert(result);
	}

	public void insert(String databaseName, String collectionName, Map<String, Object> properties) {
		Document document = new Document(properties);
		getCollection(databaseName, collectionName).insertOne(document);
	}

	public long update(String databaseName, String collectionName, BasicDBObject condition,
			Map<String, Object> updateFields) {
		Document document = new Document("$set", updateFields);
		return getCollection(databaseName, collectionName).updateMany(condition, document).getModifiedCount();
	}

	public long delete(String databaseName, String collectionName, BasicDBObject condition) {
		return getCollection(databaseName, collectionName).deleteMany(condition).getDeletedCount();
	}

	public static MongoHelper getInstance() {
		synchronized (MongoHelper.class) {
			if (INSTANCE == null) {
				synchronized (MongoHelper.class) {
					INSTANCE = new MongoHelper();
				}
			}
		}
		return INSTANCE;
	}

	public static <T> List<T> convert(MongoCursor<T> src) {
		if (src == null || !src.hasNext()) {
			return null;
		}
		List<T> result = new ArrayList<T>();
		while (src.hasNext()) {
			result.add(src.next());
		}
		return result;
	}

	public static <T> List<T> convert(MongoIterable<T> src) {
		if (src == null) {
			return null;
		}
		return convert(src.iterator());
	}

	public void closeMongoClient() {
		mongoClient.close();
	}

}
